package com.hx78.mongo.uni;

import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author linming
 */
public class MongodbCommonHelper {
    public static final List EMPTY_ARRAY = new ArrayList<>(0);

    public static Query queryForMongo2Query(QueryForMongo queryForMongo) {
        if(queryForMongo == null) {
            return new Query();
        }

        Query query = new Query();
        // criteria
        if(!CollectionUtils.isEmpty(queryForMongo.getCriteria())) {
            Map<String, Criteria> criteriaMap = queryForMongo.getCriteria();
            for (Map.Entry<String, Criteria> entry : criteriaMap.entrySet()) {
                query.addCriteria(entry.getValue());
            }
        }

        // sort
        if(queryForMongo.getSort() != null) {
            query.with(sortForMongo2Sort(queryForMongo.getSort()));
        }

        // skip
        if(queryForMongo.getSkip() > 0L) {
            query.skip(queryForMongo.getSkip());
        }

        // limit
        if(queryForMongo.getLimit() > 0L) {
            query.limit(queryForMongo.getLimit());
        }

        // hint
        if(queryForMongo.getHint() != null && queryForMongo.getHint().length() > 0) {
            query.withHint(queryForMongo.getHint());
        }

        // meta
        if(queryForMongo.getMeta() != null) {
            query.setMeta(queryForMongo.getMeta());
        }

        // collation
        if(queryForMongo.getCollation() != null) {
            query.collation(queryForMongo.getCollation());
        }

        return query;
    }

    public static QueryForMongo query2QueryForMongo(Query query) {
        if(query == null) {
            return null;
        }

        QueryForMongo queryForMongo = new QueryForMongo();
        Class<? extends Query> queryClass = query.getClass();
        try {
            // criteria
            Field criteria = queryClass.getDeclaredField("criteria");
            if(criteria != null) {
                criteria.setAccessible(true);
                if(criteria.get(query) != null) {
                    queryForMongo.setCriteria((Map<String, Criteria>) criteria.get(query));
                }
            }

            // sort
            Field sort = queryClass.getDeclaredField("sort");
            if(sort != null) {
                sort.setAccessible(true);
                if(sort.get(query) != null) {
                    queryForMongo.setSort(sort2SortForMongo((Sort) sort.get(query)));
                }
            }

            // skip
            if(query.getSkip() > 0L) {
                queryForMongo.setSkip(query.getSkip());
            }

            // limit
            if(query.getLimit() > 0L) {
                queryForMongo.setLimit(query.getLimit());
            }

            if(query.getHint() != null && query.getHint().length() > 0) {
                queryForMongo.setHint(query.getHint());
            }

            if(query.getMeta() != null) {
                queryForMongo.setMeta(query.getMeta());
            }

            if(query.getCollation().isPresent()) {
                queryForMongo.setCollation(query.getCollation().get());
            }

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return queryForMongo;
    }

    public static Sort sortForMongo2Sort(SortForMongo sortForMongo) {
        if(sortForMongo == null) {
            return Sort.unsorted();
        }

        Sort sort = Sort.unsorted();
        if(!CollectionUtils.isEmpty(sortForMongo.getOrders())) {
            sort = Sort.by(orderForMongo2Order(sortForMongo.getOrders()));
        } else if(sortForMongo.getDirection() != null && !CollectionUtils.isEmpty(sortForMongo.getProperties())) {
            sort = Sort.by(sortForMongo.getDirection(), sortForMongo.getProperties().toArray(new String[0]));
        }

        return sort;

    }

    public static SortForMongo sort2SortForMongo(Sort sort) {
        if(sort == null) {
            return null;
        }

        SortForMongo sortForMongo = new SortForMongo();
        sortForMongo.setOrders(order2OrderForMongo(sort.get().collect(Collectors.toList())));
        return sortForMongo;
    }


    public static Sort.Order orderForMongo2Order(OrderForMongo orderForMongo) {
        if(orderForMongo == null) {
            return Sort.Order.by("_id");
        }

        Sort.Order order = new Sort.Order(orderForMongo.getDirection(), orderForMongo.getProperty(), orderForMongo.getNullHandling());
        if(orderForMongo.isIgnoreCase()) {
            order.ignoreCase();
        }

//        if(orderForMongo.getDirection() != null) {
//            order.with(orderForMongo.getDirection());
//        }
//
//        if(orderForMongo.getProperty() != null) {
//            order.withProperty(order.getProperty());
//        }
//
//        if(orderForMongo.getNullHandling() != null) {
//            order.with(orderForMongo.getNullHandling());
//        }

        return order;
    }

    public static List<Sort.Order> orderForMongo2Order(List<OrderForMongo> orderForMongoList) {
        if(!CollectionUtils.isEmpty(orderForMongoList)) {
            return orderForMongoList.stream().map(item->orderForMongo2Order(item)).collect(Collectors.toList());
        }

        return EMPTY_ARRAY;
    }

    public static OrderForMongo order2OrderForMongo(Sort.Order order) {
        if (order == null) {
            return null;
        }

        OrderForMongo orderForMongo = new OrderForMongo();
        if(order.getDirection() != null) {
            orderForMongo.setDirection(order.getDirection());
        }

        if(order.getProperty() != null) {
            orderForMongo.setProperty(order.getProperty());
        }

        orderForMongo.setIgnoreCase(order.isIgnoreCase());

        if(order.getNullHandling() != null) {
            orderForMongo.setNullHandling(order.getNullHandling());
        }
        return orderForMongo;
    }

    public static List<OrderForMongo> order2OrderForMongo(List<Sort.Order> orderList) {
        if(!CollectionUtils.isEmpty(orderList)) {
            return orderList.stream().map(item->order2OrderForMongo(item)).collect(Collectors.toList());
        }

        return EMPTY_ARRAY;
    }
}
